package com.maimao.utils.excel;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.*;

public class MergeStrategy extends AbstractMergeStrategy {

    /**
     * 合并的列编号，从0开始，指定的index或自己按字段顺序数
     */
    private Set<Integer> mergeCellIndex = new HashSet<>();

    /**
     * 数据集大小（结束行位置）
     */
    private Integer maxRow = 0;

    private MergeStrategy(int size, Integer[] mergeColumns) {
    }

    public MergeStrategy(Integer maxRow, int[] mergeCellIndex) {
        if (mergeCellIndex == null || mergeCellIndex.length <= 0) {
            throw new RuntimeException("需要合并的列为空");
        }
        Arrays.stream(mergeCellIndex).forEach(item -> {
            this.mergeCellIndex.add(item);
        });
        this.maxRow = maxRow;
    }

    /**
     * 记录上一次合并的信息 key:sheetName(超过单sheet最大容量分sheet页时，sheetName默认从1递增)
     */
    private Map<Integer, Map<Integer, MergeRange>> sheetLastRow = new HashedMap();

    private static Object getCellValue(Cell cell) {
        switch (cell.getCellTypeEnum()) {
            case BOOLEAN:
                return CellValue.valueOf(cell.getBooleanCellValue()).formatAsString();
            case ERROR:
                return CellValue.getError(cell.getErrorCellValue()).formatAsString();
            case NUMERIC:
                double numericCellValue = cell.getNumericCellValue();
                if (DateUtil.isCellDateFormatted(cell)) {
                    return DateUtil.getJavaDate(numericCellValue);
                } else {
                    return numericCellValue;
                }
            case STRING:
                return cell.getRichStringCellValue().getString();
            default:
                return null;
        }
    }

    /**
     * 每个单元格都会进入
     */
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        int currentCellIndex = cell.getColumnIndex();
        int rowIndex = cell.getRowIndex();
        String sheetName = sheet.getSheetName();
        Map<Integer, MergeRange> lastRow = sheetLastRow.get(Integer.valueOf(sheetName));
        if (lastRow == null) {
            lastRow = new HashMap<Integer, MergeRange>();
            sheetLastRow.put(Integer.valueOf(sheetName), lastRow);
        }
        // 只对需合并的列进行 行合并
        if (mergeCellIndex.contains(currentCellIndex)) {
            Object currentCellValue = getCellValue(cell);
            int currentRowIndex = cell.getRowIndex();
            if (!lastRow.containsKey(currentCellIndex)) {
                // 记录首行起始位置
                lastRow.put(currentCellIndex, new MergeRange(currentCellValue, currentRowIndex, currentRowIndex, currentCellIndex, currentCellIndex));
                return;
            }
            //此列已存在上行值
            MergeRange mergeRange = lastRow.get(currentCellIndex);
            if (!(mergeRange.lastValue != null && mergeRange.lastValue.equals(currentCellValue))) {
                // 结束的位置触发下合并.
                // 同行同列不能合并，会抛异常
                if (mergeRange.startRow != mergeRange.endRow || mergeRange.startCell != mergeRange.endCell) {
                    sheet.addMergedRegionUnsafe(new CellRangeAddress(mergeRange.startRow, mergeRange.endRow, mergeRange.startCell, mergeRange.endCell));
                }
                // 更新当前列起始位置
                lastRow.put(currentCellIndex, new MergeRange(currentCellValue, currentRowIndex, currentRowIndex, currentCellIndex, currentCellIndex));
            }
            // 合并行 + 1
            mergeRange.endRow += 1;
            // 结束的位置触发下最后一次没完成的合并
            if (relativeRowIndex.equals(maxRow - 1)) {
                MergeRange lastMergeRange = lastRow.get(currentCellIndex);
                // 同行同列不能合并，会抛异常
                if (lastMergeRange.startRow != lastMergeRange.endRow || lastMergeRange.startCell != lastMergeRange.endCell) {
                    sheet.addMergedRegionUnsafe(new CellRangeAddress(lastMergeRange.startRow, lastMergeRange.endRow, lastMergeRange.startCell, lastMergeRange.endCell));
                }
            }
        }
    }
}

class MergeRange {
    public int startRow;
    public int endRow;
    public int startCell;
    public int endCell;
    public Object lastValue;

    public MergeRange(Object lastValue, int startRow, int endRow, int startCell, int endCell) {
        this.startRow = startRow;
        this.endRow = endRow;
        this.startCell = startCell;
        this.endCell = endCell;
        this.lastValue = lastValue;
    }
}